package com.hccl.service.parser;

import DM.DialogManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hccl.config.Constants;
import com.hccl.exception.updateException;
import com.hccl.exception.updateFileOperateException;
import com.hccl.mongoEntities.ParserParamEntity;
import com.hccl.util.FileOperations;
import gate.util.GateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import static com.hccl.config.Constants.*;
import static com.hccl.config.Constants.classifierTraindataPath;

/**
 * Created by sunjia on 2018/4/21.
 */
@Service
public class ParserService {

    @Autowired
    ParserPool parserPool;

    public Map<String,Object> sluParser(ParserParamEntity parserParam) throws Exception {

        //处理新增参数
        Map<String,String> newAddParam = new HashMap();
        newAddParam.put("inter_idx",parserParam.getInter_idx());
        newAddParam.put("start_time",parserParam.getStart_time());

        Map<String,Object> resultMap = new HashMap();
        Parser parser = parserPool.take();
        if (parserParam.getGreeting() != null){
            parser.greeting(parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
        } else if (parserParam.getKeypad() != null) {
            parser.keypadParser(parserParam.getKeypad(), parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
        } else {
            parser.queryParser(parserParam.getLine(), parserParam.getUserid(), parserParam.getTelephoneNumber(), newAddParam);
        }
        resultMap.put("currentSLU", parser.getCurrentSLU());
        resultMap.put("currentAction",parser.getCurrentAction());
        resultMap.put("currentGaz",parser.getCurrentGaz());
        updatekey(parser);
        return resultMap;
    }

    public  void updatekey(Parser parser){
        if (!isUpdating()){
            try {
                parserPool.add(parser);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }
        //为了保证初始化成功
        try {
            initlock.lock();
            if (parserMap.containsKey(parser.getId())){
                parserPool.add(parserMap.get(parser.getId()));
                parser.cleanupSerialController();
                System.out.println("更新编号为"+parser.getId()+"的Parser");
                parserMap.remove(parser.getId());
            }
            if (parserMap.size()==0){
                //表示更新完成
                try {
                    singallocker.lock();
                    condition.signal();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    singallocker.unlock();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            initlock.unlock();
        }
    }


    public void dmReset(ParserParamEntity parserParam){
        parserPool.getDialogManager().resetState(parserParam.getUserid());
    }

    //=======================================UPDATE====================================//
    public boolean update() throws updateException {
        try {
            if (isUpdating()) {
                System.out.println("同一时刻只允许一个更新");
                return false;
            }
            return updateAction();
        }catch (Exception e){
            throw new updateException("更新失败。系统尝试自动恢复...");
        }
    }

    //第一把锁，为了保护update同一时刻只执行一次
    private final static ReentrantLock locker = new ReentrantLock(false);

    //第二把锁,为了等待更新完成
    private final static ReentrantLock singallocker = new ReentrantLock(false);
    private final static Condition condition=singallocker.newCondition();

    //第三把锁，为了初始化map
    private final static ReentrantLock initlock = new ReentrantLock(false);

    //线程安全的map
    public ConcurrentMap<Integer, Parser> parserMap = new ConcurrentHashMap<>();
    private boolean isUpdating(){
        return locker.isLocked();
    }

    private  boolean updateAction() throws updateException {
        try {
            locker.lock();
            //增加一个初始化的读写锁
            try {
                initlock.lock();
                List<Parser> newParserList = parserPool.getNewParserList();
                for (Parser newParser:
                        newParserList) {
                    parserMap.put(newParser.getId(),newParser);
                }
                List<Parser> oldParserList = new ArrayList<Parser>();
                parserPool.getPool().drainTo(oldParserList);
                for (Parser oldparser:oldParserList){
                    parserPool.getPool().add(parserMap.get(oldparser.getId()));
                    System.out.println("更新编号为"+oldparser.getId()+"的Parser");
                    oldparser.cleanupSerialController();
                    parserMap.remove(oldparser.getId());
                }
            }catch (Exception e){//更新失败，抛出异常
                throw new updateException("更新时updateAction()出错。系统尝试自动恢复...");
            }finally {
                initlock.unlock();
            }
            //等待更新完成
            if (!parserMap.isEmpty()){
                try {
                    singallocker.lock();
                    condition.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                finally {
                    singallocker.unlock();
                }
            }
        } catch (Exception e) {
            throw e;
        }
        finally {
            locker.unlock();
        }
        return true;
    }


    //备份文件
    public void backupFiles() throws updateFileOperateException {
        //备份完毕
        FileOperations.reNameFile(gateJapePath);
        FileOperations.reNameFile(gateGazetteerPath);
        FileOperations.reNameFile(corpusPath);
        FileOperations.reNameFile(nodeInfoPath);
        FileOperations.reNameFile(classifierModelPath);
        FileOperations.reNameFile(classifierTraindataPath);

    }

    //恢复原始corpus,nodeInfo,model.bin,traindata
    public void recovery() throws updateFileOperateException {
        //恢复文件
        FileOperations.reNameFile(processPath(nodeInfoPath));
        FileOperations.reNameFile(processPath(corpusPath));
        FileOperations.reNameFile(classifierModelPath+".bak");
        FileOperations.reNameFile(classifierTraindataPath+".bak");
        FileOperations.delete(processPath(gateJapePath));
        FileOperations.delete(processPath(gateGazetteerPath));
        //并删除更新出错的文件夹
        FileOperations.delete(nodeInfoPath);
        FileOperations.delete(corpusPath);
        FileOperations.delete(classifierModelPath);
        FileOperations.delete(classifierTraindataPath);
        FileOperations.delete(gateJapePath);
        FileOperations.delete(gateGazetteerPath);
    }

    //删除备份文件corpus,nodeInfo,model.bin,traindata
    public void deleteRecoveryFile() throws updateFileOperateException {
        FileOperations.delete(processPath(nodeInfoPath));
        FileOperations.delete(processPath(corpusPath));
        FileOperations.delete(classifierModelPath+".bak");
        FileOperations.delete(classifierTraindataPath+".bak");
        FileOperations.delete(processPath(gateJapePath));
        FileOperations.delete(processPath(gateGazetteerPath));
    }

    public String processPath(String path){
        String newPath = path.substring(0,path.length()-1)+"bak";
        return newPath;
    }

}
